﻿using System;
using System.Reflection;

using zijian666.Converts.Abstractions;
using zijian666.Converts.Convertor.Base;
using zijian666.Converts.Core;
using zijian666.Converts.Extensions;

namespace zijian666.Converts.Convertor;

/// <summary>
/// <seealso cref="object"/>转换器
/// </summary>
/// <typeparam name="T"></typeparam>
public class ObjectConvertor<T> : AllowNullConvertor<T>, IFrom<object, T>
{
    private readonly bool _error;
    /// <summary>
    /// 构造函数
    /// </summary>
    public ObjectConvertor()
    {
        if (!CanBuild())
        {
            _error = true;
            return;
        }
    }

    /// <inheritdoc />
    public override uint Priority => 0;

    /// <inheritdoc />
    public ConvertResult<T> From(IConvertContext context, object input)
    {
        if (_error)
        {
            return ExceptionHelper.InstantiationFail(TypeFriendlyName, context.Settings.CultureInfo, null);
        }

        var enumerator = new KeyValueEnumerator<string, object>(context, input);
        if (!enumerator.KeyIsString || enumerator.NotEnumerator)
        {
            return context.ConvertFail(this, input);
        }
        var obj = context.Settings.CreateInstance<T>()!;
        var properties = PropertyHelper.GetByType(OutputType);
        var c1 = 0;
        var c2 = 0;
        while (enumerator.MoveNext())
        {
            c1++;
            var key = (string)enumerator.OriginalKey!;
            var prop = properties[key];
            if (prop is null)
            {
                continue;
            }
            var originalValue = enumerator.OriginalValue;
            if (prop.PropertyType.IsInstanceOfType(originalValue))
            {
                c2++;
                prop.SetValue(obj, originalValue, context.Settings.ReflectCompiler);
                continue;
            }

            var value = context.Convert(prop.PropertyType, originalValue);
            if (!value.Success)
            {
                return ExceptionHelper.PropertySetFail(TypeFriendlyName, key, originalValue, context.Settings.CultureInfo, value.Exception);
            }
            c2++;
            prop.SetValue(obj, value.Value, context.Settings.ReflectCompiler);
        }
        if (c1 > 0 && c2 == 0)
        {
            return context.ConvertFail(this, input);
        }
        return obj;

    }

    /// <summary>
    /// 是否能创建转换器
    /// </summary>
    public static bool CanBuild()
    {
        var type = typeof(T);
        if (type.IsInterface || type.IsAbstract)
        {
            return false;
        }
        if (type.IsSystemType() || type.IsValueType || type.IsEnum || type.IsArray)
        {
            return false;
        }
        if (type.Namespace is null || type.Namespace.StartsWith("System.") || type.Namespace.StartsWith("Microsoft."))
        {
            return false;
        }
        // 完全没有属性值不需要创建转换器, 没有意义
        if (type.GetProperties(BindingFlags.Public | BindingFlags.Instance).Length == 0)
        {
            return false;
        }

        try
        {
            if (type.GetConstructor(Type.EmptyTypes) is not null)
            {
                return true;
            }
            return Activator.CreateInstance(type) is not null;
        }
        catch
        {
            return false;
        }
    }
}
